package cn.spring.com.spring;

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

public class ElevenApplicationContext {

    private Class configClass;

    // 单例池
    // map存储的都是单例bean =》<bean名字，bean对象>
    private ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<>();

    // BeanDefinition对象池
    // map存储的都是BeanDefinition对象 =》<bean名字，bean定义对象>
    private ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();

    // BeanPostProcessor对象池
    private List<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();

    /**
     * 构造Spring容器（需要传递配置类）
     * @param configClass 配置类
     */
    public ElevenApplicationContext(Class configClass) {
        this.configClass = configClass;

        // 解析配置类
        // 解析@ComponentScan注解 --》扫描路径 --》扫描包下所有的类 -> BeanDefination -> BeanDefinationMap
        scan(configClass);

        // 在BeanDefinitionMap中拿出单例Bean
        for (String beanName : beanDefinitionMap.keySet()) {
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            // 找出单例Bean
            if ("singleton".equals(beanDefinition.getScope())){
                // 单例Bean
                Object o = createBean(beanName,beanDefinition);
                singletonObjects.put(beanName, o);
            }
        }

    }

    /**
     * 创建Bean对象
     * @param beanName Bean的名称
     * @param beanDefinition Bean的定义对象
     * @return Bean对象
     */
    private Object createBean(String beanName,BeanDefinition beanDefinition) {
        // 获取需要创建的Bean类型
        Class aClass = beanDefinition.getaClass();
        try {
            // 通过无参构造方法反射得到实例对象
            Object instance = aClass.getDeclaredConstructor().newInstance();

            // 依赖注入DI
            // Field是Java反射机制中的一个类，用于表示类的成员变量。
            // 它提供了许多方法来获取和操作类的字段，例如获取字段的名称、类型、修饰符、注解等。
            // 获取Bean内所有的属性
            Field[] fields = aClass.getDeclaredFields();
            for (Field field : fields) {
                // 2. 判断属性上是否有@Autowired注解
                if (field.isAnnotationPresent(Autowired.class)){
                    // 根据属性名字来找Bean对象
                    Object bean = getBean(field.getName());
                    if (bean == null){
                        throw new RuntimeException("依赖注入失败，没有找到对应的Bean对象");
                    }
                    // 依赖注入
                    // 设置属性为可访问
                    field.setAccessible(true);
                    // 设置属性值
                    // instance 是当前正在创建的 Bean 对象
                    // bean 是从容器中拿到的依赖对象
                    field.set(instance, bean);
                }
            }

            // 判断是否实现了BeanNameAware接口(感知自己的BeanName)
            if (instance instanceof BeanNameAware){
                // 强转并调用setBeanName方法拿到自己的BeanName
               ((BeanNameAware) instance).setBeanName(beanName);
            }

            // 调用BeanPostProcessor的postProcessBeforeInitialization方法
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                instance = beanPostProcessor.postProcessBeforeInitialization(instance, beanName);
            }

            // 判断是否实现了InitializingBean接口(初始化)
            if (instance instanceof InitializingBean){
                // 强转并调用afterPropertiesSet方法(初始化)
                ((InitializingBean) instance).afterPropertiesSet();
            }

            // 调用BeanPostProcessor的postProcessAfterInitialization方法
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                instance = beanPostProcessor.postProcessAfterInitialization(instance, beanName);
            }

            // 返回实例对象
            return instance;
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 在容器内通过配置类扫描Bean
     * @param configClass 配置类
     */
    private void scan(Class configClass) {
        // 解析配置类
        // 解析@ComponentScan注解 --》扫描路径 --》扫描包下所有的类
        ComponentScan componentScan = (ComponentScan) configClass.getDeclaredAnnotation(ComponentScan.class);
        String path = componentScan.value(); // 扫描路径
        path = path.replace(".", "/");

        // 扫描（使用类加载器ClassLoader）
        ClassLoader classLoader = ElevenApplicationContext.class.getClassLoader();
        URL resource = classLoader.getResource(path);
        // 转换成目录
        File file = new File(resource.getFile());
        // file是否为目录
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            for (File f : files) {
                System.out.println(f.getAbsolutePath());
                if (f.getAbsolutePath().endsWith(".class")) {
                    // 转换可用的路径格式
                    // F:\project\Spring-Eleven\target\classes\cn\spring\com\eleven\service\UserService.class
                    // cn.spring.com.eleven.service.UserService
                    String className = f.getAbsolutePath().replace("\\", "/");
                    className = className.substring(className.indexOf("cn"), className.indexOf(".class"));  // 截取类名
                    className = className.replace("/", ".");  // 替换成点号
                    System.out.println(className);
                    // 加载类
                    try {
                        Class<?> clazz = classLoader.loadClass(className);
                        // 判断是否有@Component注解
                        if (clazz.isAnnotationPresent(Component.class)) {
                            System.out.println("有@Component注解，表示当前类是一个Bean对象");

                            // 判断clazz这个类是否实现了BeanPostProcessor接口
                            if (BeanPostProcessor.class.isAssignableFrom(clazz)){
                                BeanPostProcessor beanPostProcessor = (BeanPostProcessor) clazz.getDeclaredConstructor().newInstance();
                                // 把BeanPostProcessor对象添加到BeanPostProcessor对象池
                                beanPostProcessorList.add(beanPostProcessor);
                            }

                            // 是单例bean还是原型bean
                            // 解析类，-》BeanDefinition对象

                            // 首先通过Component拿到Bean的名字
                            Component componentAnnotation = clazz.getDeclaredAnnotation(Component.class);
                            String beanName = componentAnnotation.value();

                            // 创建BeanDefinition对象
                            BeanDefinition beanDefinition = new BeanDefinition();
                            // 设置BeanDefinition的类型
                            beanDefinition.setaClass(clazz);
                            // 是否有Scope注解
                            if(clazz.isAnnotationPresent(Scope.class)){
                                // 然后通过Scope注解拿到Bean的作用域
                                Scope scopeAnnotation = clazz.getDeclaredAnnotation(Scope.class);
                                String scope = scopeAnnotation.value(); // 拿到Scope注解的值
                                beanDefinition.setScope(scope); // 设置BeanDefinition的作用域
                            } else {
                                beanDefinition.setScope("singleton"); // 默认是单例
                            }

                            // 存到BeanDefinition对象池
                            beanDefinitionMap.put(beanName, beanDefinition);
                        }
                    } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                    } catch (InvocationTargetException e) {
                        throw new RuntimeException(e);
                    } catch (InstantiationException e) {
                        throw new RuntimeException(e);
                    } catch (IllegalAccessException e) {
                        throw new RuntimeException(e);
                    } catch (NoSuchMethodException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        }
    }

    /**
     * 获取Bean对象
     * @param beanName Bean的名字
     * @return Bean对象
     */
    public Object getBean(String beanName) {
        // 在BeanDefinition对象池里是否有Bean对象
        if(beanDefinitionMap.containsKey(beanName)){
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            // 判断是单例Bean还是原型Bean
            if("singleton".equals(beanDefinition.getScope())){
                // 单例Bean，在单例池获取
                return singletonObjects.get(beanName);
            } else {
                // 原型Bean,每次都创建新的对象
                return createBean(beanName,beanDefinition);
            }
        } else {
            throw new NullPointerException("Bean不存在");
        }
    }
}
